
#include "TerrainGeom.h"
#include "Entity.h"
#include "Engine.h"
#include "Camera.h"

uint _GetPow2(uint max)
{
	uint count = 0;
	while(max != 0)
	{
		max = max >> 1;
		count++;
	}

	return 1 << (count-1);
}

ushort GetLog2(ushort a, ushort b)
{
	ushort count = 0;
	while(a > b)
	{
		a = a >> 1;
		++count;
	}

	return count;
}

//
// Conostructor
//

TerrainGeom::TerrainGeom(Entity *_parent, vector<vector<uchar>> &hMap) 
: Geometry(_parent)
{
	isTerrainGeom = true;
	numDivisions = 0;
	detail = Lo;

	parent->SetGravityModifier(0.0f);

	CreateHeightMap(hMap);
}

TerrainGeom::TerrainGeom(Entity *_parent, vector<vector<uchar>> &hMap, ushort _numDivisions, Level _detail)
: Geometry(_parent)
{
	isTerrainGeom = true;
	numDivisions = 0;
	detail = Lo;

	parent->SetGravityModifier(0.0f);

	CreateHeightMap(hMap);
	SetDetail(_numDivisions, _detail);
}

//
// SetDetail
//

void TerrainGeom::SetDetail(ushort _numDivisions, Level _detail)
{
	subLevels.clear();

	numDivisions = _numDivisions;
	detail = _detail;

	float len = sideLength / numDivisions;
	ushort max = GetLog2((ushort)sqrt((float)mesh.vertices.size()), numDivisions);

	for(ushort i = 0; i < numDivisions; ++i)
	for(ushort j = 0; j < numDivisions; ++j)
	{
		bool l = i == 0;
		bool r = i == numDivisions-1;
		bool t = j == numDivisions-1;
		bool b = j == 0;

		Real3 c(-0.5f*sideLength + (0.5f+j)*len, 0.0f, -0.5f*sideLength + (0.5f+i)*len);

		subLevels.push_back(SubLevelGeom(&mesh, c, len, max, l, r, t, b));
		subLevels[subLevels.size()-1].SetLevel(max(i,j));
	}
}

//
// _Render
//

void TerrainGeom::_Render(uchar renderFlag)
{
	float len = sideLength / numDivisions;
	short x = (short)((engine.camera->Position().x + sideLength * 0.5f) / len);
	short z = (short)((engine.camera->Position().z + sideLength * 0.5f) / len);

	for(ushort i = 0; i < numDivisions; ++i)
	for(ushort j = 0; j < numDivisions; ++j)
	{
		ushort _detail = max(abs(x-(short)j), abs(z-(short)i));
		_detail = max(0, _detail + detail);
		_detail = min(numDivisions-1, _detail);
		subLevels[i*numDivisions+j].SetLevel(_detail);
	}

	for(ushort i = 0; i < numDivisions; ++i)
	for(ushort j = 0; j < numDivisions; ++j)
	{
		if(j != 0 && subLevels[i*numDivisions+j].curLevel < subLevels[i*numDivisions+(j-1)].curLevel)
			subLevels[i*numDivisions+j].CorrectLeftEdge();
		/*if(i != 0 && subLevels[i*numDivisions+j].curLevel < subLevels[(i-1)*numDivisions+j].curLevel)
			subLevels[i*numDivisions+j].CorrectBottomEdge();
		if(j+1 != numDivisions && subLevels[i*numDivisions+j].curLevel < subLevels[i*numDivisions+(j+1)].curLevel)
			subLevels[i*numDivisions+j].CorrectRightEdge();
		if(i+1 != numDivisions && subLevels[i*numDivisions+j].curLevel < subLevels[(i+1)*numDivisions+j].curLevel)
			subLevels[i*numDivisions+j].CorrectTopEdge();*/
	}

	for(ushort i = 0; i < subLevels.size(); ++i)
		subLevels[i]._Render(renderFlag);
}

//
// CreateHeightMap
//

#define GRID_SQUARE 1.0f

void TerrainGeom::CreateHeightMap(vector<vector<uchar>> &hMap)
{
	uint size = hMap.size();
	uint newSize = _GetPow2(size-1);

	sideLength = newSize * GRID_SQUARE;

	// Create grid
	_DrawGrid(&mesh, newSize, newSize, GRID_SQUARE);
	newSize++;

	for(uint i = 0; i < newSize; ++i)
	for(uint j = 0; j < newSize; ++j)
	{
		if(i != 0 && j != 0 && i != newSize-1 && j != newSize-1)
			mesh.vertices[i*newSize+j].position.y = GRID_SQUARE * 0.1f * hMap[i][j];
		parent->AddBBoxPoint(mesh.vertices[i*newSize+j].position);
	}

	// Calculate some normals
	mesh.CalculateVertexNormals();
}

//
// GetHeight
//

float TerrainGeom::GetHeight(float x, float z)
{
	if(abs(x) > 0.5f * sideLength ||
		 abs(z) > 0.5f * sideLength)
	 return 0.0f;

	float minLength = mesh.vertices[1].position.z - mesh.vertices[0].position.z;

	ushort maxDivisions = (ushort)sqrt((float)mesh.vertices.size());

	ushort a = (uint)((z + 0.5f * sideLength) / minLength);
	ushort b = (uint)((x + 0.5f * sideLength) / minLength);

	float pA = (z - mesh.vertices[b*maxDivisions+a].position.z) / minLength;
	float pB = (x - mesh.vertices[b*maxDivisions+a].position.x) / minLength;

	float y;

	if(pA < 0 || a > maxDivisions-2)
	{
		if(pB < 0 || b > maxDivisions-2)
		{
			y = mesh.vertices[b*maxDivisions+a].position.y;
		}
		else
		{
			y = mesh.vertices[b*maxDivisions+a].position.y * (1.0f-pB) + mesh.vertices[(b+1)*maxDivisions+ a].position.y * pB;
		}
	}
	else
	{
		if(pB < 0 || b > maxDivisions-2)
		{
			y = mesh.vertices[b*maxDivisions+a].position.y * (1.0f-pA) + mesh.vertices[b*maxDivisions+(a+1)].position.y * pA;
		}
		else
		{
			y = mesh.vertices[ b   *maxDivisions+ a   ].position.y * (1.0f-pA) + mesh.vertices[ b   *maxDivisions+(a+1)].position.y * pA +
					mesh.vertices[(b+1)*maxDivisions+ a   ].position.y * (1.0f-pA) + mesh.vertices[(b+1)*maxDivisions+(a+1)].position.y * pA +
					mesh.vertices[ b   *maxDivisions+ a   ].position.y * (1.0f-pB) + mesh.vertices[(b+1)*maxDivisions+ a   ].position.y * pB +
					mesh.vertices[ b   *maxDivisions+(a+1)].position.y * (1.0f-pB) + mesh.vertices[(b+1)*maxDivisions+(a+1)].position.y * pB;
			y *= 0.25f;
		}
	}

	return y;
}




//
// Constructor
//

SubLevelGeom::SubLevelGeom(Mesh *hMap, const Real3 &_center, float _sideLength, ushort _maxLevel, 
													 bool _isLeftEdge, bool _isRightEdge, bool _isTopEdge, bool _isBottomEdge) : Geometry(0)
{
	center     = _center;
	sideLength = _sideLength;
	maxLevel   = _maxLevel;
	curLevel   = _maxLevel-1;

	isLeftEdge   = _isLeftEdge;
	isRightEdge  = _isRightEdge;
	isTopEdge    = _isTopEdge;
	isBottomEdge = _isBottomEdge;

	CreateMaxMesh(hMap);
}

//
// SetLevel
//

void SubLevelGeom::SetLevel(ushort level)
{
	if(curLevel != level)
	{
		curLevel = level;
		edgeDivisions = (ushort)pow(2.0f, maxLevel - curLevel);

		// Clears old terrain
		mesh.Clear();

		// Create new grid base
		_DrawGrid(&mesh, edgeDivisions, edgeDivisions, sideLength / edgeDivisions, center);

		uint jump = (uint)pow(2.0f, curLevel);
		uint q = edgeDivisions*jump+1;
		uint s = edgeDivisions+1;

		// Set heights from max detail mesh
		for(uint j = 0; j < s; ++j)
		for(uint k = 0; k < s; ++k)
		{
			mesh.vertices[j*s+k].position.y = maxMesh.vertices[(j*jump)*q+(k*jump)].position.y;
			mesh.vertices[j*s+k].normal     = maxMesh.vertices[(j*jump)*q+(k*jump)].normal;
			#ifdef USE_TEX
			mesh.vertices[j*s+k].tex        = maxMesh.vertices[(j*jump)*q+(k*jump)].tex;
			#endif
		}

		hasCorrectedBottomEdge = false;
		hasCorrectedTopEdge = false;
		hasCorrectedLeftEdge = false;
		hasCorrectedRightEdge = false;
	}
}

//
// CorrectEdges
//

void SubLevelGeom::CorrectBottomEdge()
{
	if(!hasCorrectedBottomEdge)
	{
		hasCorrectedBottomEdge = true;

		for(ushort i = 0; i < (edgeDivisions >> 1); ++i)
		{
			mesh.vertices[(i*2+1)*edgeDivisions].position.y = (mesh.vertices[(i*2)*edgeDivisions].position.y + mesh.vertices[(i*2+2)*edgeDivisions].position.y) * 0.5f;
			mesh.vertices[(i*2+1)*edgeDivisions].normal     = (mesh.vertices[(i*2)*edgeDivisions].normal     + mesh.vertices[(i*2+2)*edgeDivisions].normal)     * 0.5f;
		}
	}
}

void SubLevelGeom::CorrectLeftEdge()
{
	if(!hasCorrectedLeftEdge)
	{
		hasCorrectedLeftEdge = true;

		for(ushort i = 0; i < (edgeDivisions >> 1); ++i)
		{
			mesh.vertices[i*2+1].position.y = (mesh.vertices[i*2].position.y + mesh.vertices[i*2+2].position.y) * 0.5f;
			mesh.vertices[i*2+1].normal     = (mesh.vertices[i*2].normal     + mesh.vertices[i*2+2].normal)     * 0.5f;
		}
	}
}

void SubLevelGeom::CorrectRightEdge()
{
	if(!hasCorrectedRightEdge)
	{
		hasCorrectedRightEdge = true;

		uint offset = edgeDivisions * (edgeDivisions-1);

		for(ushort i = 0; i < (edgeDivisions >> 1); ++i)
		{
			mesh.vertices[i*2+1+offset].position.y = (mesh.vertices[i*2+offset].position.y + mesh.vertices[i*2+2+offset].position.y) * 0.5f;
			mesh.vertices[i*2+1+offset].normal     = (mesh.vertices[i*2+offset].normal     + mesh.vertices[i*2+2+offset].normal)     * 0.5f;
		}
	}
}

void SubLevelGeom::CorrectTopEdge()
{
	if(!hasCorrectedTopEdge)
	{
		hasCorrectedTopEdge = true;

		uint offset = edgeDivisions-1;

		for(ushort i = 0; i < (edgeDivisions >> 1); ++i)
		{
			mesh.vertices[(i*2+1)*edgeDivisions+offset].position.y = (mesh.vertices[(i*2)*edgeDivisions+offset].position.y + mesh.vertices[(i*2+2)*edgeDivisions+offset].position.y) * 0.5f;
			mesh.vertices[(i*2+1)*edgeDivisions+offset].normal     = (mesh.vertices[(i*2)*edgeDivisions+offset].normal     + mesh.vertices[(i*2+2)*edgeDivisions+offset].normal)     * 0.5f;
		}
	}
}

//
// _Render
//

void SubLevelGeom::_Render(uchar renderFlag)
{
	mesh.CreateBuffers();
	mesh.LoadBuffers(renderFlag);
	//maxMesh.CreateBuffers();
	//maxMesh.LoadBuffers(renderFlag);
}

//
// CreateMaxMesh
//

#define _ERROR_ 0.0005f

void SubLevelGeom::CreateMaxMesh(Mesh *hMap)
{
	edgeDivisions = (ushort)pow(2.0f, maxLevel);

	ushort maxDivisions = (ushort)sqrt((float)hMap->vertices.size());
	float minLength = hMap->vertices[1].position.z              - hMap->vertices[0].position.z;
	float maxLength = hMap->vertices[maxDivisions-1].position.z - hMap->vertices[0].position.z;

	// Create Grid
	_DrawGrid(&maxMesh, edgeDivisions, edgeDivisions, sideLength / edgeDivisions, center);

	ushort s = edgeDivisions+1;

	// Set heights for max detail mesh
	for(uint j = 0; j < s; ++j)
	for(uint k = 0; k < s; ++k)
	{
		ushort a = (uint)((maxMesh.vertices[j*s+k].position.z + 0.5f * maxLength) / minLength);
		ushort b = (uint)((maxMesh.vertices[j*s+k].position.x + 0.5f * maxLength) / minLength);

		float pA = (maxMesh.vertices[j*s+k].position.z - hMap->vertices[b*maxDivisions+a].position.z) / minLength;
		float pB = (maxMesh.vertices[j*s+k].position.x - hMap->vertices[b*maxDivisions+a].position.x) / minLength;

		float y;
		Real3 n;
		#ifdef USE_TEX
		Real2 t;
		#endif
		//if(abs(pA) < _ERROR_)
		if(pA < 0 || a > maxDivisions-2)
		{
			//if(abs(pB) < _ERROR_)
			if(pB < 0 || b > maxDivisions-2)
			{
				y = hMap->vertices[b*maxDivisions+a].position.y;
				n = hMap->vertices[b*maxDivisions+a].normal;
				#ifdef USE_TEX
				t = hMap->vertices[b*maxDivisions+a].tex;
				#endif
			}
			else
			{
				y = hMap->vertices[b*maxDivisions+a].position.y * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+ a].position.y * pB;
				n = hMap->vertices[b*maxDivisions+a].normal     * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+ a].normal * pB;
				#ifdef USE_TEX
				t = hMap->vertices[b*maxDivisions+a].tex        * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+ a].tex * pB;
				#endif
			}
		}
		else
		{
			//if(abs(pB) < _ERROR_)
			if(pB < 0 || b > maxDivisions-2)
			{
				y = hMap->vertices[b*maxDivisions+a].position.y * (1.0f-pA) + hMap->vertices[b*maxDivisions+(a+1)].position.y * pA;
				n = hMap->vertices[b*maxDivisions+a].normal     * (1.0f-pA) + hMap->vertices[b*maxDivisions+(a+1)].normal * pA;
				#ifdef USE_TEX
				t = hMap->vertices[b*maxDivisions+a].tex        * (1.0f-pA) + hMap->vertices[b*maxDivisions+(a+1)].tex * pA;
				#endif
			}
			else
			{
				y = hMap->vertices[ b   *maxDivisions+ a   ].position.y * (1.0f-pA) + hMap->vertices[ b   *maxDivisions+(a+1)].position.y * pA +
						hMap->vertices[(b+1)*maxDivisions+ a   ].position.y * (1.0f-pA) + hMap->vertices[(b+1)*maxDivisions+(a+1)].position.y * pA +
						hMap->vertices[ b   *maxDivisions+ a   ].position.y * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+ a   ].position.y * pB +
						hMap->vertices[ b   *maxDivisions+(a+1)].position.y * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+(a+1)].position.y * pB;
				y *= 0.25f;

				n = hMap->vertices[ b   *maxDivisions+ a   ].normal * (1.0f-pA) + hMap->vertices[ b   *maxDivisions+(a+1)].normal * pA +
						hMap->vertices[(b+1)*maxDivisions+ a   ].normal * (1.0f-pA) + hMap->vertices[(b+1)*maxDivisions+(a+1)].normal * pA +
						hMap->vertices[ b   *maxDivisions+ a   ].normal * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+ a   ].normal * pB +
						hMap->vertices[ b   *maxDivisions+(a+1)].normal * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+(a+1)].normal * pB;
				n *= 0.25f;

				#ifdef USE_TEX
				t = hMap->vertices[ b   *maxDivisions+ a   ].tex * (1.0f-pA) + hMap->vertices[ b   *maxDivisions+(a+1)].tex * pA +
						hMap->vertices[(b+1)*maxDivisions+ a   ].tex * (1.0f-pA) + hMap->vertices[(b+1)*maxDivisions+(a+1)].tex * pA +
						hMap->vertices[ b   *maxDivisions+ a   ].tex * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+ a   ].tex * pB +
						hMap->vertices[ b   *maxDivisions+(a+1)].tex * (1.0f-pB) + hMap->vertices[(b+1)*maxDivisions+(a+1)].tex * pB;
				t *= 0.25f;
				#endif
			}
		}

		maxMesh.vertices[j*s+k].position.y = y;
		maxMesh.vertices[j*s+k].normal     = n;
		#ifdef USE_TEX
		maxMesh.vertices[j*s+k].tex        = t;
		#endif
	}
}
